package com.sunxueqi.framework.aspect;

import java.lang.reflect.Method;
import java.sql.Timestamp;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import com.sunxueqi.common.constant.CfnConstants;
import com.sunxueqi.common.constant.StatusConstants;
import com.sunxueqi.common.utils.AddressUtils;
import com.sunxueqi.common.utils.IpUtils;
import com.sunxueqi.common.utils.KeyGenerate;
import com.sunxueqi.common.utils.MessageUtils;
import com.sunxueqi.common.utils.SecurityUtils;
import com.sunxueqi.common.utils.ServletUtils;
import com.sunxueqi.framework.annotation.Log;
import com.sunxueqi.framework.manager.AsyncFactory;
import com.sunxueqi.project.log.vo.OperationLog;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;

// 使之成为切面类
@Aspect
@Component
public class OperateLogAspect {

	/**
	 * 记录执行前的时间
	 */
	private long startTime = 0L;
	
	/**
	 * 定义切入点，切入点为使用了该注解的所有方法
	 */
	@Pointcut("@annotation(com.sunxueqi.framework.annotation.Log)")
	public void logPointCut() {}

	/**
	 * 
	 * @throws Throwable 
	 */
	@Around("logPointCut()")
	public Object doAfter(ProceedingJoinPoint joinPoint) throws Throwable {
		startTime = System.currentTimeMillis();
		Object result = joinPoint.proceed();
		handleOperateLog(joinPoint, null, result, System.currentTimeMillis() - startTime);
		return result;
	}
	
	@AfterThrowing(value = "logPointCut()", throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
		handleOperateLog(joinPoint, e, null, System.currentTimeMillis() - startTime);
	}
	
	/**
	 * 记录操作日志
	 * @param joinPoint
	 * @param e
	 * @param jsonResult
	 * @param cost
	 */
	protected void handleOperateLog(final JoinPoint joinPoint, final Exception e, Object jsonResult, long cost) {
		// 获取注解
		Log controllerLog = getAnnotationLog(joinPoint);
		if(controllerLog == null) {
			return;
		}
		
		OperationLog operationLog = new OperationLog();
		operationLog.setId(KeyGenerate.getKeyId());
		operationLog.setStatus(StatusConstants.SUCCESSFUL);
		String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
		String address = AddressUtils.getCityInfoByIp(ip);
		operationLog.setIp(ip);
		operationLog.setLocation(address);
		operationLog.setReturnMessage(JSONUtil.toJsonStr(jsonResult));
		operationLog.setCost(cost);
		operationLog.setUrl(ServletUtils.getRequest().getRequestURI());
		
		String loginUserId = SecurityUtils.getLoginUserId();
		if(StrUtil.isNotBlank(loginUserId)) {
			operationLog.setOperator(loginUserId);
		}
		
		if(e != null) {
			operationLog.setStatus(StatusConstants.FAILED);
			operationLog.setErrorMessage(StrUtil.subPre(e.getMessage(), 2000));
		}
		
		String className = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		operationLog.setMethod(StrUtil.format("{}.{}()", className, methodName));
		operationLog.setRequestMethod(ServletUtils.getRequest().getMethod());
		operationLog.setDeleteStatus(StatusConstants.NORMAL);
		operationLog.setCreateTime(new Timestamp(System.currentTimeMillis()));
		getControllerMethodDescription(joinPoint, controllerLog, operationLog);
		
		ThreadUtil.execAsync(AsyncFactory.recordOperateLog(operationLog));
		
	}
	
	/**
	 * 获取注解信息
	 * @param joinPoint
	 * @return
	 */
	private Log getAnnotationLog(JoinPoint joinPoint) {
		Signature signature = joinPoint.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Method method = methodSignature.getMethod();
		if(method != null) {
			return method.getAnnotation(Log.class);
		}
		return null;
	}

	/**
	 * 获取controller对方法的描述
	 * @param joinPoint
	 * @param log
	 * @param operationLog
	 */
	private void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperationLog operationLog) {
		operationLog.setOperationType(String.valueOf(log.businessType().ordinal()));
		operationLog.setTitle(log.title());
		//operationLog.setOperator(log.operatorType().ordinal());
	}
}
